home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 (Walnut Creek) / Aminet - June 1993 [Walnut Creek].iso / usenet / sources / volume90 / unix / ls_3_1 / part03 < prev   
Internet Message Format  |  1990-04-10  |  47KB

  1. Path: xanth!cs.odu.edu!Amiga-Request
  2. From: Amiga-Request@cs.odu.edu (Amiga Sources/Binaries Moderator)
  3. Newsgroups: comp.sources.amiga
  4. Subject: v90i133: ls 3.1 - UNIX-like ls utility, Part03/03
  5. Message-ID: <12130@xanth.cs.odu.edu>
  6. Date: 10 Apr 90 21:27:17 GMT
  7. Sender: tadguy@cs.odu.edu
  8. Reply-To: kim@uts.amdahl.com (Kim E. DeVaughn)
  9. Lines: 1470
  10. Approved: tadguy@cs.odu.edu (Tad Guy)
  11. X-Mail-Submissions-To: Amiga@cs.odu.edu
  12. X-Post-Discussions-To: comp.sys.amiga
  13.  
  14. Submitted-by: kim@uts.amdahl.com (Kim E. DeVaughn)
  15. Posting-number: Volume 90, Issue 133
  16. Archive-name: unix/ls-3.1/part03
  17.  
  18. #!/bin/sh
  19. # This is a shell archive.  Remove anything before this line, then unpack
  20. # it by saving it into a file and typing "sh file".  To overwrite existing
  21. # files, type "sh file -c".  You can also feed this as standard input via
  22. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  23. # will see the following message at the end:
  24. #        "End of archive 3 (of 3)."
  25. # Contents:  src/ls.c
  26. # Wrapped by tadguy@xanth on Tue Apr 10 17:22:51 1990
  27. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  28. if test -f 'src/ls.c' -a "${1}" != "-c" ; then 
  29.   echo shar: Will not clobber existing file \"'src/ls.c'\"
  30. else
  31. echo shar: Extracting \"'src/ls.c'\" \(43452 characters\)
  32. sed "s/^X//" >'src/ls.c' <<'END_OF_FILE'
  33. X/* ---------------------------------------------------------------------
  34. X  LS.C -- Source code for the "improved" directory utility.
  35. X
  36. X  Author: Justin V. McCormick
  37. X
  38. X  V1.0    August 1986 Written from scratch, my first Amiga project.
  39. X  V2.0  November 1988 Revised for Lattice 5.0 and made 1.3 compatible.
  40. X  V2.1  December 1988 Minor size reduction, fixed a few bugs from 2.0.
  41. X  V2.2  December 1988 Fixed status return and multiple wildcard pathnames.
  42. X  V3.0  July 25, 1989 Instant sorting, best-fit output, new features.
  43. X  V3.1  July 29, 1989 Bug fixes, new output width and height options.
  44. X
  45. XNotice:
  46. X
  47. X  This program is placed in the public domain with the
  48. Xunderstanding that the author makes no claims or guarantees with
  49. Xregard to its suitability for any given application, and that
  50. Xthe author assumes no responsibility for damages incurred by its
  51. Xusage.  Any resemblance of this program to any other program,
  52. Xeither living or dead, is purely coincidental.
  53. X
  54. X  Feel free to borrow this code and make millions of dollars
  55. Xfrom its sale or commercial use, but please give credit where
  56. Xcredit is due.
  57. X
  58. XSynopsis:
  59. X
  60. X  Features intelligent columnar listing, versatile sort options,
  61. XUNIX-style pattern matching, recursive subdirectory listing, and
  62. Xmore!
  63. X
  64. XUsage:
  65. X        LS [options] [pattern1] [pattern2] ...
  66. X
  67. XBugs and Limitations:
  68. X
  69. X  LS cannot pattern match devices (like "dh*:"), since this
  70. Xwould involve another level of recursion and parsing the Device
  71. XList.  LS shows signs of the same "creeping featurism" which
  72. Xafflicted the UNIX command, evident from its bloated code size.
  73. X
  74. XChanges From 1.0 to 2.0:
  75. X
  76. X o Source code prototyped, linted, traced, optimized, tweaked, etc.
  77. X o Made resident ("pseudo-pure") by linking with cres.o from LC 5.0.
  78. X o High-volume routines recoded in assembly (lssup.a).
  79. X o Now handles multiple paths/files on a command line, up to 30.
  80. X o New sort flags, including no sort.
  81. X o Enhanced wildcards, understands complex *.?*.* expressions now.
  82. X o More efficient ExNext() performance, less ram used for recursion.
  83. X o SIGBREAKF_CTRL_C signal (Ctrl-C) cleanly aborts at any point now.
  84. X o Command line parser handles quoted pathnames now (LC 5.0 benefit).
  85. X o Short listing finally auto-adjusts to new console window sizes!
  86. X o Pen color escape codes bypassed when redirecting long output.
  87. X o Sorting by size or date is also subsorted alphabetically now.
  88. X o Long listing shows new 1.3 file attributes, plus comment indicator.
  89. X o File dates are now in international format, YY-MM-DD.
  90. X o Fixed listings with files datestamped after 99-12-31 (overflow).
  91. X o Fixed listings with files datestamped before 78-01-01 (time < 0).
  92. X
  93. XChanges From 2.0 to 2.1
  94. X
  95. X o Fixed the show comment feature, a last minute bug in 2.0.
  96. X o Fixed the "Unknown option ''" message problem.
  97. X o Optimized the assembly branches, reduced code size another few bytes.
  98. X
  99. XChanges From 2.1 to 2.2
  100. X
  101. X o Fixed erroneous Status returns.
  102. X o Fixed bug with multiple wildcarded pathnames.
  103. X o Compiled with LC 5.0 Patch 3 and CAPE 2.0, saved another 46 bytes.
  104. X o Eliminated an extra stpcpy(), saved another few bytes.
  105. X
  106. XChanges From 2.2 to 3.0
  107. X
  108. X o Added many new command line options!  See Usage descriptions and docs.
  109. X o "Instant" output sorting, now using an on-the-fly insertion sort.
  110. X o New Short listing technique is row-by-row, redirects to PRT: or file.
  111. X o Better columnization of short listings, uses best-fit optimization.
  112. X o Formatted output parsing system implemented for long listings.
  113. X o Improved ^C handling, now breaks immediately in mid-output.
  114. X o New command line parser, now handles multiple options mixed with names.
  115. X o Added separate pattern matching for directories and files.
  116. X o Now inhibits system requesters from popping up by default.
  117. X o Added custom cres.o module for more size savings.
  118. X o Now uses Exec List functions for smaller, faster code.
  119. X o Fixed random cli_ReturnCode and pr_Result2 value CLI returns.
  120. X o Rewrote many sections, further code cleaning/commenting.
  121. X o Eliminated main(), LS handles the _main() function for smaller code.
  122. X o Compiled with 5.03.90 (beta) Lattice compiler, saved a few bytes.
  123. X
  124. XChanges From 3.0 to 3.1
  125. X o Fixed random errors from uninitialized argument count in GetCLIArgs().
  126. X o New -X and -Y options to specify short format columns and rows.
  127. X
  128. X --------------------------------------------------------------------- */
  129. X
  130. X/* ---------------------- LS SOURCE ---------------------------------- */
  131. X
  132. X/* Use Real strlen unless you comment out the following */
  133. X#define strlen strlen
  134. X
  135. X/* Maximum number of command line arguments */
  136. X#define MAXARG 32
  137. X#define PADTABSIZE 100
  138. X#define WORKSIZE 300
  139. X
  140. X#include "WORK:ls.h"
  141. X
  142. X/* Extern CODE from lssup.a */
  143. Xextern BYTE *aindex __ARGS((BYTE *, BYTE));
  144. Xextern LONG  __stdargs asprintf __ARGS((BYTE *, BYTE *,...));
  145. Xextern LONG CompareDateStamps __ARGS((struct DateStamp *, struct DateStamp *));
  146. Xextern LONG CompFibs __ARGS((LONG, struct FileInfoBlock *, struct FileInfoBlock *));
  147. Xextern LONG FillFibEntry __ARGS((struct List *, struct FileInfoBlock *));
  148. Xextern LONG GetFileString __ARGS((BYTE *, BYTE *));
  149. Xextern LONG GetPathString __ARGS((BYTE *, BYTE *));
  150. Xextern LONG iswild __ARGS((BYTE *));
  151. Xextern LONG wildmatch __ARGS((BYTE *, BYTE *));
  152. Xextern VOID *myalloc __ARGS((LONG));
  153. Xextern VOID FibFileDate __ARGS((struct DateStamp *, BYTE *, BYTE *));
  154. Xextern VOID GetWinBounds __ARGS((LONG *, LONG *));
  155. Xextern VOID InsertFibNode __ARGS((struct List *, struct FibEntry *));
  156. Xextern VOID MakePathString __ARGS((struct FileLock *, BYTE *));
  157. Xextern VOID myfree __ARGS((VOID *));
  158. Xextern VOID NoFileExit __ARGS((BYTE *));
  159. Xextern VOID NoMemExit __ARGS((VOID));
  160. X
  161. X/* Local DATA */
  162. Xstruct DateStamp theolddate;     /* Show files older than this date */
  163. Xstruct DateStamp thenewdate;     /* Show files newer than this date */
  164. Xstruct FileHandle *ConOut;       /* Standard output */
  165. Xstruct FileHandle *ConIn;        /* Standard input */
  166. Xstruct FileLock *CurFLock;       /* Global Directory lock */
  167. Xstruct FileInfoBlock *GFibp;     /* Global FIB for Examine/ExNext */
  168. XAPTR OldWindowPtr;               /* Copy of what was in pr_WindowPtr */
  169. X
  170. X/* User flags, See ls.h for bit definitions */
  171. XULONG LSFlags = SHOWDIRS | SHOWFILES;
  172. X
  173. XLONG gdircount;                 /* Grand total # of dirs for Recursive */
  174. XLONG gfilecount;                /* Grand total # of files found */
  175. XLONG gtotblocks;                /* Grand total # of blocks */
  176. XLONG gtotbytes;                 /* Grand total # of bytes */
  177. X
  178. XLONG dircount;                  /* Number of directories found */
  179. XLONG filecount;                 /* Number of files found */
  180. XLONG totblocks;                 /* total # of blocks */
  181. XLONG totbytes;                  /* total # of bytes */
  182. XLONG maxnamlen;                 /* Longest name found */
  183. XLONG sortkey;                   /* 0=alpha, 1=size, 2=date */
  184. X
  185. XBYTE padtab[PADTABSIZE];        /* Column table tab amounts */
  186. XBYTE thePath[WORKSIZE];         /* Current filename space */
  187. XBYTE theDirPat[WORKSIZE];       /* Dirname pattern workspace */
  188. XBYTE theFilePat[WORKSIZE];      /* Filename pattern workspace */
  189. XBYTE workstr[WORKSIZE+64];      /* Temp string space */
  190. X
  191. XBYTE theblksstr[20];
  192. XBYTE thedatestr[12];
  193. XBYTE theprotstr[12];
  194. XBYTE thesizestr[20];
  195. XBYTE thetimestr[12];
  196. X
  197. XLONG CurWinRows;                /* Window bounds, determined at runtime */
  198. XLONG CurWinCols;
  199. X
  200. X/* Initialized Strings */
  201. XBYTE Author[]  = "\23333mLS 3.1\2330m PD Freeware by Justin V. McCormick 89-07-29";
  202. XBYTE usage[]   = "\n  Usage: LS [-options <args>] [path1] [path2] ...\n";
  203. XBYTE usage0[]  = "\tc  Show filenotes           D  Show dirs last\n";
  204. XBYTE usage1[]  = "\td  Dirs only                H  No headings and subtotals\n";
  205. XBYTE usage2[]  = "\tf  Files only               I  No page prompts\n";
  206. XBYTE usage3[]  = "\tk  No ANSI display codes    M  Mixed output files and dirs\n";
  207. XBYTE usage4[]  = "\tl  Long listing             N <name> Show newer than entry\n";
  208. XBYTE usage5[]  = "\tn  No sort                  O <name> Show older than entry\n";
  209. XBYTE usage6[]  = "\tp  Permit system requests   P  Show full pathnames\n";
  210. XBYTE usage7[]  = "\tr  Reverse sort             R  Recursive listing\n";
  211. XBYTE usage8[]  = "\ts  Sort by size             T  Total all subtotals\n";
  212. XBYTE usage9[]  = "\tt  Sort by date             X <wide> Set output columns\n";
  213. XBYTE usage10[] = "\tv <pattern> Avoid pattern   Y <high> Set output rows\n";
  214. XBYTE usage11[] = "\tF <format> Format output (default: \042";
  215. XBYTE usage12[] = "\042)\n";
  216. X
  217. X/* Date and output format strings */
  218. XBYTE baddatestr[]     = "00-00-00";
  219. XBYTE badtimestr[]     = "00:00:00";
  220. XBYTE datepat[]        = "%02ld-%02ld-%02ld";
  221. XBYTE timepat[]        = "%02ld:%02ld:%02ld";
  222. XBYTE dayspermonth[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
  223. XBYTE deffmtstr[40]    = "%p %d %t %4b %8s %n\\n";
  224. XBYTE LongFmtStr[]     = "%ld";
  225. XBYTE totalfmtstr[]    = "Dirs:%-4ld Files:%-4ld Blocks:%-5ld Bytes:%-8ld\n";
  226. XBYTE TotHeaderMsg[]   = "Totals:\n";
  227. XBYTE ColonStr[]       = ":";
  228. XBYTE SlashStr[]       = "/";
  229. XBYTE RamNameStr[]     = "RAM:";
  230. X
  231. X/* ANSI color control strings */
  232. XBYTE penstr0[]        = "\2330m";       /* Reset CON:   */
  233. XBYTE penstr1[]        = "\2330 p";      /* Cursor off   */
  234. XBYTE penstr2[]        = "\233 p";       /* Cursor on    */
  235. XBYTE penstr3[]        = "\23333m";      /* Color 3 NORM */
  236. XBYTE penstr4[]        = "\2331;33;40m"; /* Color 3 BOLD */
  237. XBYTE penstr5[]        = "\2331;31;40m"; /* Color 1 BOLD */
  238. X
  239. X/* CON: command sequence for window bounds report */
  240. XBYTE gwbrstr[4] = {  0x9b, '0', ' ', 'q' };
  241. X
  242. X/* Newline and "" source */
  243. XBYTE NLine[4] = { 10, 0, 0, 0 };
  244. X
  245. X/* Error messages */
  246. XBYTE BadOptFmtStr[]   = "Unknown option: -%s\n";
  247. XBYTE ErrFmtStr[]      = "LS: %s, Error #%ld\n";
  248. XBYTE NoExamFmtStr[]   = "Cannot examine file or directory, Error #%ld\n";
  249. XBYTE NoFindFmtStr[]   = "'%s' not found";
  250. XBYTE NoRAMMsg[]       = "No RAM";
  251. XBYTE NoWildPathMsg[]  = "Unable to pattern match paths";
  252. XBYTE VolEmptyMsg[]    = "Volume or directory is empty.\n";
  253. XBYTE NoMatchMsg[]     = "No match.\n";
  254. X
  255. X/* Pointers to usage strings for quick easy output */
  256. XBYTE *usagestrs[] =
  257. X{
  258. X  Author, usage, usage0, usage1, usage2, usage3, usage4, usage5,
  259. X  usage6, usage7, usage8, usage9, usage10, usage11, deffmtstr, usage12, 0L
  260. X};
  261. X
  262. XBYTE *thefmtstr = deffmtstr;    /* Format string pointer for long output */
  263. XBYTE *curpath;
  264. XBYTE *theAntiPat;        /* Avoid pattern string */
  265. X
  266. X/* Local CODE */
  267. XBYTE *GetDecNum __ARGS((BYTE *, LONG *));
  268. XLONG CmpDateBounds __ARGS((struct DateStamp *));
  269. XLONG GetFileDate __ARGS((BYTE *, struct DateStamp *));
  270. XLONG ParseCmdOptions __ARGS((LONG, LONG, BYTE **));
  271. Xstruct FibEntry *AllocFib __ARGS((VOID));
  272. Xstruct FibEntry *ModNextFib __ARGS((struct FibEntry *, LONG));
  273. Xstruct List *GetDir __ARGS((struct FileLock *, struct FileInfoBlock *));
  274. XVOID CleanUp __ARGS((BYTE *, LONG, LONG));
  275. XVOID DirIt __ARGS((struct FileLock *, BYTE *));
  276. XVOID FreeAllFibs __ARGS((struct List *));
  277. XVOID GetCLIArgs __ARGS((BYTE *, LONG *, BYTE **));
  278. XVOID LListDir __ARGS((struct List *));
  279. XVOID LListEntry __ARGS((struct FileInfoBlock *));
  280. XVOID LongList __ARGS((LONG *, LONG *, struct List *));
  281. XVOID PagePrompt __ARGS((LONG, LONG));
  282. XVOID ParseFormatOpts __ARGS((struct FileInfoBlock *));
  283. XVOID SetConPen __ARGS((BYTE *));
  284. XVOID SListDir __ARGS((struct List *));
  285. XVOID TestBreak __ARGS((VOID));
  286. XVOID Usage __ARGS((VOID));
  287. XVOID WCHR __ARGS((BYTE *));
  288. XVOID WSTR __ARGS((BYTE *));
  289. XVOID _main __ARGS((BYTE *));
  290. X
  291. X/* --------------------------------------------------------------------
  292. X * Allocate a FibEntry structure and associated FileInfoBlock.
  293. X * -------------------------------------------------------------------- */
  294. Xstruct FibEntry *AllocFib ()
  295. X{
  296. X  struct FibEntry *tfibp;
  297. X
  298. X  if ((tfibp = myalloc ((LONG)(sizeof (struct FibEntry) + sizeof (struct FileInfoBlock)))) != 0)
  299. X  {
  300. X    tfibp->fe_Fib = (struct FileInfoBlock *)((ULONG)tfibp + sizeof (struct FibEntry));
  301. X  }
  302. X  else
  303. X    LSFlags |= BREAKFLAG;
  304. X  return (tfibp);
  305. X}
  306. X
  307. X/* --------------------------------------------------------------------
  308. X * Use AmigaDOS to output a string to the stdout channel
  309. X * -------------------------------------------------------------------- */
  310. XVOID WSTR (tstring)
  311. X  BYTE *tstring;
  312. X{
  313. X  LONG i;
  314. X
  315. X  i = strlen (tstring);
  316. X  if (i > 0)
  317. X  {
  318. X    (VOID) Write ((BPTR)ConOut, tstring, i);
  319. X  }
  320. X}
  321. X
  322. X/* --------------------------------------------------------------------
  323. X * Use AmigaDOS to put a character to the stdout
  324. X * -------------------------------------------------------------------- */
  325. XVOID WCHR (ch)
  326. X  BYTE *ch;
  327. X{
  328. X  (VOID) Write ((BPTR)ConOut, ch, 1L);
  329. X}
  330. X
  331. X/* --------------------------------------------------------------------
  332. X * Check to see if the user hit ^C
  333. X * -------------------------------------------------------------------- */
  334. XVOID TestBreak ()
  335. X{
  336. X  ULONG oldsig;
  337. X
  338. X  oldsig = SetSignal (0L, (LONG)SIGBREAKF_CTRL_C);
  339. X  if ((oldsig & SIGBREAKF_CTRL_C) != 0)
  340. X  {
  341. X    WSTR ("\2330m\233 p**BREAK\n");
  342. X    LSFlags |= BREAKFLAG;
  343. X  }
  344. X}
  345. X
  346. X/* --------------------------------------------------------------------
  347. X * Prompt the user to hit return, wait till return is hit
  348. X * -------------------------------------------------------------------- */
  349. XVOID PagePrompt (page, maxpage)
  350. X  LONG page, maxpage;
  351. X{
  352. X  if (CurWinCols > maxnamlen && page > 1 && (LSFlags & NOINTERACT) == 0)
  353. X  {
  354. X    WSTR ("\2337m -- MORE -- ");
  355. X    if (CurWinCols >= 27)
  356. X      WSTR ("Press Return: ");
  357. X    WSTR ("\2330m\233 p");
  358. X    (VOID) Read ((BPTR)ConIn, workstr, 1L);
  359. X    WSTR ("\2330 p\233F\233K");
  360. X    TestBreak ();
  361. X  }
  362. X
  363. X  if ((LSFlags & (NOHEADERS|BREAKFLAG)) == 0)
  364. X  {
  365. X    if ((LSFlags & CONSOLE) != 0)
  366. X    {
  367. X      WSTR ("\2330 p\2334;33m");
  368. X    }
  369. X    (VOID) asprintf (workstr, "Page %ld of %ld:", page, maxpage);
  370. X    WSTR (workstr);
  371. X    SetConPen (penstr0);
  372. X    WSTR (NLine);
  373. X  }
  374. X}
  375. X
  376. X/* -------------------------------------------------------------------- */
  377. Xstruct FibEntry *ModNextFib(tfibp, rows)
  378. X  struct FibEntry *tfibp;
  379. X  LONG rows;
  380. X{
  381. X  LONG i;
  382. X
  383. X  for (i = 0; i < rows && tfibp->fe_Node.mln_Succ != 0; i++)
  384. X  {
  385. X    tfibp = (struct FibEntry *)tfibp->fe_Node.mln_Succ;
  386. X  }
  387. X  return(tfibp);
  388. X}
  389. X
  390. X/* --------------------------------------------------------------------
  391. X * set CON: character color to default Pen1 colors 
  392. X * -------------------------------------------------------------------- */
  393. XVOID SetConPen (penstr)
  394. X  BYTE *penstr;
  395. X{
  396. X  if ((LSFlags & CONSOLE) != 0)
  397. X    WSTR (penstr);
  398. X}
  399. X
  400. X/* #define DEBUGSLD 1 */
  401. X/* --------------------------------------------------------------------
  402. X * List a FibEntry list in a compact fashion
  403. X * -------------------------------------------------------------------- */
  404. XVOID SListDir (fibheadp)
  405. X  struct List *fibheadp;
  406. X{
  407. X  LONG avglen;
  408. X  LONG colcnt;
  409. X  LONG currow;
  410. X  LONG dfcount;
  411. X  LONG i, j, wlen;
  412. X  LONG maxcol;
  413. X  LONG maxpage;
  414. X  LONG maxrow;
  415. X  LONG maxwinrow;
  416. X  LONG pagecnt;
  417. X  LONG rowcnt;
  418. X  LONG tlen;
  419. X  LONG totlen;
  420. X  struct FibEntry *hfibp, *tfibp;
  421. X
  422. X  SetConPen (penstr1);        /* Turn the cursor off since it will blink anyway */
  423. X  GetWinBounds (&colcnt, &currow); /* Get current window size */
  424. X
  425. X  if (CurWinCols == 0)
  426. X    CurWinCols = colcnt;
  427. X  if (CurWinRows == 0)
  428. X    CurWinRows = currow;
  429. X
  430. X/* Make a average-case WxH estimate for # of display columns */
  431. X  for (totlen = dfcount = 0, hfibp = (struct FibEntry *)fibheadp->lh_Head;
  432. X       hfibp->fe_Node.mln_Succ != 0;
  433. X       hfibp = (struct FibEntry *)hfibp->fe_Node.mln_Succ)
  434. X  {
  435. X    if (hfibp->fe_Fib->fib_DirEntryType > 0 && (LSFlags & CONSOLE) == 0)
  436. X      (VOID) strcat (hfibp->fe_Fib->fib_FileName, SlashStr);
  437. X    totlen += strlen (hfibp->fe_Fib->fib_FileName);
  438. X    dfcount++;
  439. X  }
  440. X
  441. X/* Calc average length of all entries */
  442. X  avglen = totlen / dfcount;            /* Avg name length = totlen/numentries */
  443. X  if ((totlen % dfcount) != 0)
  444. X    avglen++;
  445. X
  446. X  if ((CurWinCols) <= maxnamlen)        /* Longest name wider than window? */
  447. X    maxcol = 1;                         /* Yep, just print one column */
  448. X  else
  449. X  {
  450. X  /* Else maxcols = winwidth/namewidth */
  451. X    for (maxcol = 0, colcnt = CurWinCols; colcnt >= avglen; maxcol++)
  452. X    {
  453. X      colcnt -= avglen + 2;
  454. X    }
  455. X  }
  456. X#ifdef DEBUGSLD
  457. X  asprintf(workstr, "avg:%ld max:%ld\n", avglen, maxnamlen); WSTR(workstr);
  458. X#endif
  459. X
  460. X/* Dry run output avg-case WxH table to see if it needs adjusting */
  461. X  for (;;)
  462. X  {
  463. X  /* Clear out previous padtab */
  464. X    memset(padtab, 0, PADTABSIZE);
  465. X
  466. X  /* Number of rows = total entries / entries per row */
  467. X    maxrow = dfcount / maxcol;
  468. X    if ((dfcount % maxcol) != 0)        /* Round up if non-integral */
  469. X      maxrow++;
  470. X#ifdef DEBUGSLD
  471. X    asprintf(workstr, "avg: %ld rows by %ld cols\n", maxrow, maxcol); WSTR(workstr);
  472. X#endif
  473. X
  474. X    for (rowcnt = 0, hfibp = (struct FibEntry *)fibheadp->lh_Head;
  475. X         rowcnt < maxrow && hfibp->fe_Node.mln_Succ != 0;
  476. X         rowcnt++, hfibp = (struct FibEntry *)hfibp->fe_Node.mln_Succ)
  477. X    {
  478. X      for (colcnt = 0, tfibp = hfibp;
  479. X           colcnt < maxcol && tfibp->fe_Node.mln_Succ != 0;
  480. X           colcnt++, tfibp = ModNextFib (tfibp, maxrow))
  481. X      {
  482. X        tlen = strlen (tfibp->fe_Fib->fib_FileName);
  483. X        if (tlen > padtab[colcnt])
  484. X          padtab[colcnt] = tlen;
  485. X      }
  486. X
  487. X    /* If this is the first row, calc actual maxcol/maxrow for this dfcount */
  488. X      if (rowcnt == 0)
  489. X      {
  490. X        maxcol = colcnt;
  491. X        maxrow = dfcount / maxcol;
  492. X        if ((dfcount % maxcol) != 0)    /* Round up if non-integral */
  493. X          maxrow++;
  494. X      }
  495. X    }
  496. X
  497. X  /* Calculate actual total width by adding up width of all columns */
  498. X    for (colcnt = totlen = 0; (colcnt + 1) < maxcol; colcnt++)
  499. X    {
  500. X      totlen += (LONG)padtab[colcnt] + 2;
  501. X#ifdef DEBUGSLD
  502. X      asprintf(workstr, "padtab[%ld]=%ld\n", colcnt, (LONG)padtab[colcnt]); WSTR(workstr);
  503. X#endif
  504. X    }
  505. X    totlen += (LONG)padtab[colcnt];
  506. X#ifdef DEBUGSLD
  507. X    asprintf(workstr, "padtab[%ld]=%ld\n", colcnt, (LONG)padtab[colcnt]); WSTR(workstr);
  508. X    asprintf(workstr, "totlen %ld\n", totlen); WSTR(workstr);
  509. X#endif
  510. X
  511. X  /* if More than one column and 
  512. X   * total width of all columns is greater > our window width,
  513. X   * then decrease number of display columns
  514. X   */
  515. X    if (maxcol > 1 && totlen > CurWinCols)
  516. X    {
  517. X      maxcol--;
  518. X#ifdef DEBUGSLD
  519. X      asprintf(workstr, "new maxcol:%ld\n", maxcol); WSTR(workstr);
  520. X#endif
  521. X    }
  522. X    else
  523. X      break;
  524. X  }
  525. X#ifdef DEBUGSLD
  526. X  asprintf(workstr, "adjusted: maxrow:%ld maxcol:%ld (winwidth:%ld totlen:%ld)\n", maxrow, maxcol, CurWinCols, totlen); WSTR(workstr);
  527. X#endif
  528. X
  529. X/* Calc number of pages */
  530. X  maxwinrow = CurWinRows - 3;
  531. X  if (maxwinrow <= 0)
  532. X    maxwinrow = 1;
  533. X  pagecnt = 1;
  534. X  maxpage = maxrow / maxwinrow;
  535. X  if ((maxrow % maxwinrow) != 0)
  536. X    maxpage++;
  537. X
  538. X/* Do actual output scan */
  539. X  for (rowcnt = 0, currow = maxwinrow, hfibp = (struct FibEntry *)fibheadp->lh_Head;
  540. X       (LSFlags & BREAKFLAG) == 0 && rowcnt < maxrow && hfibp->fe_Node.mln_Succ != 0;
  541. X       TestBreak(), rowcnt++, currow++, hfibp = (struct FibEntry *)hfibp->fe_Node.mln_Succ)
  542. X  {
  543. X    if (maxpage > 1 && currow == maxwinrow)
  544. X    {
  545. X      currow = 0;
  546. X      if ((LSFlags & CONSOLE) != 0)
  547. X      {
  548. X        PagePrompt (pagecnt, maxpage);
  549. X        if ((LSFlags & BREAKFLAG) != 0)
  550. X          break;
  551. X      }
  552. X      pagecnt++;
  553. X    }
  554. X
  555. X    for (colcnt = 0, tfibp = hfibp;
  556. X         colcnt < maxcol && tfibp->fe_Node.mln_Succ != 0;
  557. X         colcnt++)
  558. X    {
  559. X      if (tfibp->fe_Fib->fib_DirEntryType < 0)
  560. X      {
  561. X      /* Print a file entry */
  562. X        (VOID) stpcpy (workstr, tfibp->fe_Fib->fib_FileName);
  563. X        wlen = strlen (workstr);
  564. X      }
  565. X      else
  566. X      {
  567. X      /* Print a directory entry */
  568. X        workstr[0] = 0;
  569. X        wlen = strlen (tfibp->fe_Fib->fib_FileName);
  570. X        if ((LSFlags & CONSOLE) != 0)
  571. X          (VOID) strcat (workstr, penstr3);
  572. X        (VOID) strcat (workstr, tfibp->fe_Fib->fib_FileName);
  573. X        if ((LSFlags & CONSOLE) != 0)
  574. X          (VOID) strcat (workstr, penstr0);
  575. X      }
  576. X
  577. X    /* Move along list to the next entry, mod maxcol, print this entry */
  578. X      tfibp = ModNextFib (tfibp, maxrow);
  579. X
  580. X    /* If this is not the last column, pad with spaces till we get to next column */
  581. X      if ((colcnt + 1) < maxcol && tfibp->fe_Node.mln_Succ != 0)
  582. X      {
  583. X        for (i = (LONG)padtab[colcnt] + 1, j = strlen (workstr); i >= wlen; i--, j++)
  584. X          workstr[j] = ' ';
  585. X        workstr[j] = 0;
  586. X      }
  587. X    /* Output the final entry */
  588. X      WSTR (workstr);
  589. X    }
  590. X
  591. X  /* Filled this row, start next down */
  592. X    WCHR (NLine);
  593. X  }
  594. X
  595. X  SetConPen (penstr2);        /* Turn cursor back on */
  596. X}
  597. X
  598. X/* -------------------------------------------------------------------- */
  599. XBYTE *GetDecNum (cp, spccnt)
  600. X  BYTE *cp;
  601. X  LONG *spccnt;
  602. X{
  603. X  for (*spccnt = 0; *cp >= '0' && *cp <= '9'; cp++)
  604. X  {
  605. X    *spccnt = *spccnt * 10 + (LONG)*cp - '0';
  606. X  }
  607. X  return (cp);
  608. X}
  609. X
  610. X/* -------------------------------------------------------------------- */
  611. XVOID ParseFormatOpts (fib)
  612. X  struct FileInfoBlock *fib;
  613. X{
  614. X  BYTE *cp1, *cp2, *reps;
  615. X  BYTE *pathend, *thenamestr;
  616. X  LONG i, spccnt;
  617. X
  618. X  i = strlen (curpath);
  619. X  pathend = curpath + i;
  620. X  if (i > 1 && *(curpath + i - 1) != ':')
  621. X  {
  622. X    *(curpath + i) = '/';
  623. X    i++;
  624. X    *(curpath + i) = 0;
  625. X  }
  626. X  thenamestr = curpath + strlen(curpath);
  627. X  cp2 = thenamestr;
  628. X  if (fib->fib_DirEntryType > 0 && (LSFlags & CONSOLE) != 0)
  629. X    cp2 = stpcpy (cp2, penstr3);
  630. X  cp2 = stpcpy (cp2, fib->fib_FileName);
  631. X  if (fib->fib_DirEntryType > 0 && (LSFlags & CONSOLE) != 0)
  632. X    (VOID) stpcpy (cp2, penstr0);
  633. X
  634. X  for (cp1 = workstr, cp2 = thefmtstr; *cp2 != 0; cp2++)
  635. X  {
  636. X    if (*cp2 != '%' && *cp2 != '\\')
  637. X      *cp1++ = *cp2;
  638. X    else
  639. X    {
  640. X      if (*cp2 == '%')
  641. X      {
  642. X        cp2++;
  643. X        cp2 = GetDecNum (cp2, &spccnt);
  644. X        if (spccnt > 99)
  645. X        {
  646. X          spccnt = 99;
  647. X        }
  648. X          
  649. X        switch (*cp2)
  650. X        {
  651. X          case 'p':
  652. X            reps = theprotstr;
  653. X            break;
  654. X          case 'd':
  655. X            reps = thedatestr;
  656. X            break;
  657. X          case 't':
  658. X            reps = thetimestr;
  659. X            break;
  660. X          case 'b':
  661. X            reps = theblksstr;
  662. X            break;
  663. X          case 's':
  664. X            reps = thesizestr;
  665. X            break;
  666. X          case 'n':
  667. X            if (fib->fib_DirEntryType > 0 && (LSFlags & CONSOLE) != 0 && spccnt > 0)
  668. X              spccnt += 7;
  669. X            if ((LSFlags & FULLPATHNAMES) != 0)
  670. X              reps = curpath;
  671. X            else
  672. X              reps = thenamestr;
  673. X            break;
  674. X          case '%':
  675. X            *cp1++ = '%';
  676. X            *cp1++ = 0;
  677. X          default:
  678. X            reps = &NLine[1];
  679. X            break;
  680. X        }
  681. X        for (i = strlen(reps); i < spccnt; i++)
  682. X          *cp1++ = ' ';
  683. X        cp1 = stpcpy (cp1, reps);
  684. X      }
  685. X      else
  686. X      {
  687. X        cp2++;
  688. X        switch (*cp2)
  689. X        {
  690. X          case 'n':
  691. X            *cp1++ = '\n';
  692. X            break;
  693. X          case 't':
  694. X            *cp1++ = '\t';
  695. X            break;
  696. X          case '\\':
  697. X            *cp1++ = '\\';
  698. X            break;
  699. X          default:
  700. X            break;
  701. X        }
  702. X        *cp1 = 0;
  703. X      }
  704. X    }
  705. X  }
  706. X  WSTR (workstr);
  707. X
  708. X  if ((LSFlags & NOTEFLAG) != 0 && fib->fib_Comment[0] != 0)
  709. X  {
  710. X    SetConPen(penstr3);
  711. X    (VOID)asprintf(workstr, "/* %s */\n", fib->fib_Comment);
  712. X    WSTR (workstr);
  713. X    SetConPen(penstr0);
  714. X  }
  715. X  *pathend = 0;
  716. X}
  717. X
  718. X/* --------------------------------------------------------------------
  719. X * Verbosely list a particular FibEntry
  720. X * -------------------------------------------------------------------- */
  721. XVOID LListEntry (fib)
  722. X  struct FileInfoBlock *fib;
  723. X{
  724. X  LONG i, pmodes;
  725. X
  726. X  pmodes = fib->fib_Protection & 0xff;
  727. X  (VOID)stpcpy (theprotstr, "chsparwed");
  728. X  for (i = 3; i >=  0; i--)
  729. X  {
  730. X    if ((pmodes & (1 << i)) != 0)
  731. X      theprotstr[8 - i] = '-';
  732. X    if ((pmodes & (1 << (i + 4))) == 0)
  733. X      theprotstr[4 - i] = '-';
  734. X  }
  735. X
  736. X  if (fib->fib_Comment[0] == 0)
  737. X    theprotstr[0] = '-';
  738. X
  739. X  FibFileDate (&fib->fib_Date, thedatestr, thetimestr);
  740. X
  741. X  if (fib->fib_DirEntryType > 0)
  742. X  {
  743. X    (VOID)stpcpy (theblksstr, "0");
  744. X    (VOID)stpcpy (thesizestr, "Dir");
  745. X  }
  746. X  else
  747. X  {
  748. X    (VOID) asprintf (theblksstr, LongFmtStr, fib->fib_NumBlocks);
  749. X    (VOID) asprintf (thesizestr, LongFmtStr, fib->fib_Size);
  750. X  }
  751. X    
  752. X  ParseFormatOpts (fib);
  753. X}
  754. X
  755. X/* --------------------------------------------------------------------
  756. X * List a directory in a verbose informative manner
  757. X * -------------------------------------------------------------------- */
  758. XVOID LListDir (fibheadp)
  759. X  struct List *fibheadp;
  760. X{
  761. X  struct FibEntry *tfibp;
  762. X
  763. X  SetConPen(penstr1);
  764. X
  765. X  totblocks = totbytes = 0;
  766. X  for (tfibp = (struct FibEntry *)fibheadp->lh_Head;
  767. X       tfibp->fe_Node.mln_Succ != 0;
  768. X       tfibp = (struct FibEntry *)tfibp->fe_Node.mln_Succ)
  769. X  {
  770. X    TestBreak();
  771. X    if ((LSFlags & BREAKFLAG) != 0)
  772. X      return;
  773. X    LListEntry (tfibp->fe_Fib);
  774. X    if (tfibp->fe_Fib->fib_DirEntryType < 0)
  775. X    {
  776. X      totblocks += tfibp->fe_Fib->fib_NumBlocks;
  777. X      totbytes += tfibp->fe_Fib->fib_Size;
  778. X    }
  779. X  }
  780. X
  781. X  if ((LSFlags & (BREAKFLAG | NOHEADERS)) == 0)
  782. X  {
  783. X    (VOID) asprintf (workstr, totalfmtstr, dircount, filecount, totblocks, totbytes);
  784. X    WSTR (workstr);
  785. X  }
  786. X
  787. X  SetConPen(penstr2);
  788. X}
  789. X
  790. X/* -------------------------------------------------------------------- */
  791. XLONG CmpDateBounds (tdate)
  792. X  struct DateStamp *tdate;
  793. X{
  794. X  if ((LSFlags & SHOWNEWERTHAN) != 0)
  795. X  {
  796. X    if (CompareDateStamps(&thenewdate, tdate) >= 0)
  797. X      return (0L);
  798. X  }
  799. X  if ((LSFlags & SHOWOLDERTHAN) != 0)
  800. X  {
  801. X    if (CompareDateStamps(tdate, &theolddate) >= 0)
  802. X      return (0L);
  803. X  }
  804. X  return (1L);
  805. X}
  806. X
  807. X/* --------------------------------------------------------------------
  808. X * Free up memory allocated to a linked list of FibEntrys
  809. X * -------------------------------------------------------------------- */
  810. XVOID FreeAllFibs (fibheadp)
  811. X  struct List *fibheadp;
  812. X{
  813. X  struct FibEntry *tfibp;
  814. X
  815. X  if (fibheadp != 0)
  816. X  {
  817. X    while (fibheadp->lh_Head->ln_Succ != 0)
  818. X    {
  819. X      tfibp = (struct FibEntry *)RemTail(fibheadp);
  820. X      myfree (tfibp);
  821. X    }
  822. X
  823. X  /* Now free the MinList itself */
  824. X    myfree (fibheadp);
  825. X  }
  826. X}
  827. X
  828. X/* --------------------------------------------------------------------
  829. X * Allocate and fill a linked list of FileInfoBlocks
  830. X * -------------------------------------------------------------------- */
  831. Xstruct List *GetDir (lockp, fibp)
  832. X  struct FileLock *lockp;
  833. X  struct FileInfoBlock *fibp;
  834. X{
  835. X  BYTE *thepat;                /* Pattern pointer to dir or file pattern */
  836. X  LONG matchstat;              /* Result of wildmatch() */
  837. X  LONG nextstat;               /* Status of ExNext() */
  838. X  LONG tempnamlen;             /* Compare length for strings */
  839. X  LONG dfcount;
  840. X  struct List *fibhead;        /* Temp list of Fibs created */
  841. X  struct List *dirhead;        /* Temp list of dirs if SHOWDIRS == 0 */
  842. X
  843. X  maxnamlen = dfcount = dircount = filecount = 0L;
  844. X
  845. X/* Initialize an exec list of nodes, zero entries */
  846. X  if ((fibhead = myalloc ((LONG)sizeof(struct MinList))) == 0)
  847. X    return (0L);
  848. X  NewList (fibhead);
  849. X
  850. X/* Allocate an separate list for directories that don't match specs */
  851. X  if ((dirhead = myalloc((LONG)sizeof(struct MinList))) == 0)
  852. X    goto BADALLOC;
  853. X  NewList (dirhead);
  854. X
  855. X  do
  856. X  {
  857. X    TestBreak ();
  858. X    if ((LSFlags & BREAKFLAG) != 0)
  859. X      goto GOODRET;
  860. X
  861. X  /* If we got something */
  862. X    if ((nextstat = ExNext ((BPTR)lockp, fibp)) != 0)
  863. X    {
  864. X      dfcount++;
  865. X
  866. X    /* If the entry is wanted bump count of files or directories */
  867. X      if (CmpDateBounds (&fibp->fib_Date) != 0)
  868. X      {
  869. X        if (fibp->fib_DirEntryType > 0) /* It's a directory */
  870. X          thepat = theDirPat;
  871. X        else
  872. X          thepat = theFilePat;
  873. X
  874. X        matchstat = wildmatch (fibp->fib_FileName, thepat);
  875. X        if ((LSFlags & ANTIMATCH) != 0 && matchstat != 0)
  876. X          matchstat = wildmatch (fibp->fib_FileName, theAntiPat) ^ 1;
  877. X
  878. X    if (matchstat == 0)    /* No match? Then move on */
  879. X      continue;
  880. X
  881. X        if (fibp->fib_DirEntryType > 0) /* It's a directory */
  882. X        {
  883. X          if (FillFibEntry (dirhead, fibp) == 0)
  884. X            goto BADALLOC;
  885. X          if ((LSFlags & SHOWDIRS) != 0)
  886. X          {
  887. X            dircount++;
  888. X            gdircount++;
  889. X          }
  890. X          else
  891. X            continue;
  892. X        }
  893. X        else                            /* It's a file entry */
  894. X        {
  895. X          if ((LSFlags & SHOWFILES) != 0)
  896. X          {
  897. X            filecount++;
  898. X            gfilecount++;
  899. X            gtotblocks += fibp->fib_NumBlocks;
  900. X            gtotbytes += fibp->fib_Size;
  901. X          }
  902. X          else                /* Don't want this file, move on to next entry */
  903. X            continue;
  904. X        }
  905. X
  906. X      /* See if this is the longest filename for later use in listing */
  907. X        tempnamlen = strlen (fibp->fib_FileName);
  908. X        if (tempnamlen > maxnamlen)
  909. X          maxnamlen = tempnamlen;
  910. X
  911. X      /* Allocate another FibEntry, put the info inside */
  912. X        if (FillFibEntry (fibhead, fibp) == 0)
  913. X          goto BADALLOC;
  914. X      }
  915. X    }
  916. X  } while (nextstat != 0);
  917. X
  918. X/* No entries found? print message and return FALSE */
  919. X  if ((dircount + filecount) == 0)
  920. X  {
  921. X    if ((LSFlags & NOHEADERS) == 0)
  922. X    {
  923. X      if (dfcount == 0)
  924. X        WSTR (VolEmptyMsg);
  925. X      else
  926. X        WSTR (NoMatchMsg);
  927. X    }
  928. X  }
  929. X  else
  930. X  {
  931. X    if ((LSFlags & (SHOWDIRS|SHOWFILES)) != 0)
  932. X    {
  933. X      if ((LSFlags & LONGLIST) == 0)  /* Short listing wanted */
  934. X        SListDir (fibhead);
  935. X      else                            /* Full listing */
  936. X        LListDir (fibhead);
  937. X    }
  938. X  }
  939. X
  940. XGOODRET:
  941. X  FreeAllFibs (fibhead);
  942. X  return (dirhead);
  943. X
  944. XBADALLOC:
  945. X  FreeAllFibs (fibhead);
  946. X  FreeAllFibs (dirhead);
  947. X  return (0L);
  948. X}
  949. X
  950. X/* --------------------------------------------------------------------
  951. X * Given a directory name and a lock on that directory, create a list
  952. X * of entries.  Recursively decends into subdirectories if flaged.
  953. X * -------------------------------------------------------------------- */
  954. XVOID DirIt (curlock, dirname)
  955. X  struct FileLock *curlock;
  956. X  BYTE *dirname;
  957. X{
  958. X  BYTE *subdir;
  959. X  LONG dnamlen;
  960. X  struct FibEntry *tfibp;
  961. X  struct FileLock *sublock;
  962. X  struct List *fibheadp;
  963. X
  964. X/* Try to fill FileInfoBlock, bomb if not readable for some reason */
  965. X  if (Examine ((BPTR)curlock, GFibp) == 0)
  966. X  {
  967. X    (VOID) asprintf (workstr, NoExamFmtStr, IoErr());
  968. X    WSTR (workstr);
  969. X    return;
  970. X  }
  971. X
  972. X/* Put directory header for each listing, if we know the name && recursive */
  973. X  if (dirname[0] != 0 && (LSFlags & LISTALL) != 0 && (LSFlags & NOHEADERS) == 0)
  974. X  {
  975. X    if ((LSFlags & CONSOLE) != 0)
  976. X     (VOID)asprintf (workstr, "\23330;41m %s \2330m\n", dirname);
  977. X    else
  978. X     (VOID)asprintf (workstr, "%s\n", dirname);
  979. X    WSTR (workstr);
  980. X  }
  981. X
  982. X/* If this is a single file list it verbosely */
  983. X  if (GFibp->fib_DirEntryType < 0)
  984. X  {
  985. X    if ((LSFlags & SHOWFILES) != 0)
  986. X    {
  987. X      (VOID) GetPathString (thePath, thePath);
  988. X      LListEntry (GFibp);
  989. X    }
  990. X  }
  991. X  else /* It is a directory entry */
  992. X  {
  993. X  /* Allocate, fill, and display a dir of entries, check for no ram or abort */
  994. X    if ((fibheadp = GetDir (curlock, GFibp)) != 0)
  995. X    {
  996. X    /* Recursively descend any subdirs in this list, if wanted */
  997. X      if ((LSFlags & LISTALL) != 0)
  998. X      {
  999. X        for (tfibp = (struct FibEntry *)fibheadp->lh_Head;
  1000. X              tfibp->fe_Node.mln_Succ != 0;
  1001. X              tfibp = (struct FibEntry *)tfibp->fe_Node.mln_Succ)
  1002. X        {
  1003. X          TestBreak ();
  1004. X          if ((LSFlags & BREAKFLAG) != 0)
  1005. X            break;
  1006. X
  1007. X          if (tfibp->fe_Fib->fib_DirEntryType > 0)
  1008. X          {
  1009. X          /* Alloc length of path + 1 + newdir name +1 + length of maxname + 1 for NULL
  1010. X           * + 1 for null + 3 for rounding.
  1011. X           */
  1012. X            dnamlen = (LONG)(strlen (dirname) + strlen (tfibp->fe_Fib->fib_FileName) + 36);
  1013. X            if ((subdir = myalloc ((LONG)dnamlen)) != 0)
  1014. X            {
  1015. X              if (dirname[0] != 0)
  1016. X              {
  1017. X                (VOID) stpcpy (subdir, dirname);
  1018. X                dnamlen = strlen (dirname) - 1;
  1019. X                if (dirname[dnamlen] != ':' && dirname[dnamlen] != '/')
  1020. X                {
  1021. X                  (VOID) strcat (subdir, SlashStr);
  1022. X                }
  1023. X              }
  1024. X              (VOID) strcat (subdir, tfibp->fe_Fib->fib_FileName);
  1025. X
  1026. X              if ((sublock = (struct FileLock *)Lock (subdir, (LONG)ACCESS_READ)) != 0)
  1027. X              {
  1028. X                if ((LSFlags & NOHEADERS) == 0)
  1029. X                  WCHR (NLine);                 /* Put a blank line between directories */
  1030. X        
  1031. X        curpath = subdir;
  1032. X
  1033. X                DirIt (sublock, subdir); /* Recurse into this subdirectory */
  1034. X
  1035. X                UnLock ((BPTR)sublock);     /* Unlock our sublock */
  1036. X              }
  1037. X              myfree (subdir);           /* Free the current namespace */
  1038. X            }
  1039. X          }
  1040. X        }
  1041. X      }
  1042. X    /* Free up this fib list */
  1043. X      FreeAllFibs (fibheadp);
  1044. X    }
  1045. X  }
  1046. X}
  1047. X
  1048. X/* -------------------------------------------------------------------- */
  1049. XVOID GetCLIArgs (line, argc, argv)
  1050. X  BYTE *line;
  1051. X  LONG *argc;
  1052. X  BYTE **argv;
  1053. X{
  1054. X  BYTE **pargv, *qarg;
  1055. X
  1056. X  *argc = 0;
  1057. X  while (*argc < MAXARG)
  1058. X  {
  1059. X    while (*line == ' ' || *line == '\t' || *line == '\n')
  1060. X      line++;
  1061. X    if (*line == 0)
  1062. X      break;
  1063. X    pargv = &argv[*argc];
  1064. X    *argc += 1;
  1065. X
  1066. X    if (*line == '"')
  1067. X    {
  1068. X      qarg = line;
  1069. X      line += 1;
  1070. X      *pargv = line;                 /* ptr inside quoted string */
  1071. X      while (*line != 0 && *line != '"')
  1072. X        line++;
  1073. X      if (*line == 0)                /* Hit end of string without quote! */
  1074. X      {
  1075. X        *pargv = qarg;               /* Must be okay */
  1076. X        break;
  1077. X      }
  1078. X      else
  1079. X        *line++ = 0;                 /* terminate arg ontopof quote */
  1080. X    }
  1081. X    else                             /* non-quoted arg */
  1082. X    {
  1083. X      *pargv = line;
  1084. X      while (*line != 0 && !(*line == ' ' || *line == '\t' || *line == '\n'))
  1085. X        line++;
  1086. X      if (*line == 0)
  1087. X        break;
  1088. X      else
  1089. X        *line++ = 0;                 /* terminate arg */
  1090. X    }
  1091. X  }                                  /* while */
  1092. X}
  1093. X
  1094. X/* -------------------------------------------------------------------- */
  1095. XLONG GetFileDate(name, ptime)
  1096. X  char *name;
  1097. X  struct DateStamp *ptime;
  1098. X{
  1099. X  LONG status;
  1100. X  struct FileLock *flock;
  1101. X  struct FileInfoBlock *fib;
  1102. X
  1103. X  status = 0;
  1104. X  if ((fib = myalloc ((LONG)sizeof(struct FileInfoBlock))) != 0)
  1105. X  {
  1106. X    if ((flock = (struct FileLock *)Lock (name, (LONG)ACCESS_READ)) != 0)
  1107. X    {
  1108. X      if (Examine ((BPTR)flock, fib) != 0)
  1109. X      {
  1110. X        *ptime = fib->fib_Date;     /* Copy the Date structure */
  1111. X        status = 1;
  1112. X      }
  1113. X      UnLock ((BPTR)flock);
  1114. X    }
  1115. X    myfree (fib);
  1116. X  }
  1117. X  return (status);
  1118. X}
  1119. X
  1120. X/* --------------------------------------------------------------------
  1121. X * Deallocate and close everything
  1122. X * -------------------------------------------------------------------- */
  1123. XVOID CleanUp (exit_msg, exit_status, result2)
  1124. X  BYTE *exit_msg;
  1125. X  LONG exit_status, result2;
  1126. X{
  1127. X  BYTE workstr[WORKSIZE];
  1128. X  struct Process *procp;
  1129. X
  1130. X/* Make sure we unlock any locks we created! */
  1131. X  if (CurFLock != 0 && (LSFlags & PATHNAMED) != 0)
  1132. X    UnLock ((BPTR)CurFLock);
  1133. X
  1134. X/* Free our fib */
  1135. X  myfree (GFibp);
  1136. X
  1137. X  if (*exit_msg != 0)
  1138. X  {
  1139. X    (VOID) asprintf (workstr, ErrFmtStr, exit_msg, result2);
  1140. X    WSTR (workstr);
  1141. X  }
  1142. X
  1143. X/* Put windowptr back */
  1144. X  procp = (struct Process *) FindTask (0L);
  1145. X  procp->pr_WindowPtr = OldWindowPtr;
  1146. X
  1147. X/* Set return status, exit */
  1148. X  procp->pr_Result2 = result2;
  1149. X  exit ((int)exit_status);
  1150. X}
  1151. X
  1152. X/* --------------------------------------------------------------------
  1153. X * Explain how to use.
  1154. X * -------------------------------------------------------------------- */
  1155. XVOID Usage ()
  1156. X{
  1157. X  LONG i;
  1158. X
  1159. X  for (i = 0; usagestrs[i] != 0; i++)
  1160. X    WSTR (usagestrs[i]);
  1161. X  CleanUp (&NLine[1], 20L, 120L);
  1162. X}
  1163. X
  1164. X/* -------------------------------------------------------------------- */
  1165. XLONG ParseCmdOptions (ncnt, argc, argv)
  1166. X  LONG ncnt, argc;
  1167. X  BYTE **argv;
  1168. X{
  1169. X  BYTE tmpmsg[4];
  1170. X  LONG i, cnt, len;
  1171. X  struct Process *procp;
  1172. X
  1173. X  cnt = ncnt;      /* Current arg number that contains the options */
  1174. X  ncnt += 1;       /* Advance to next arg */
  1175. X
  1176. X  for (i = 1, len = strlen (argv[cnt]); i < len; i++)
  1177. X  {
  1178. X    switch (argv[cnt][i])
  1179. X    {
  1180. X      case '?':
  1181. X      case 'h':
  1182. X        Usage ();
  1183. X        break;
  1184. X      case 'D':
  1185. X        LSFlags |= FILESFIRST;
  1186. X        break;
  1187. X      case 'F':
  1188. X        if (argc < (ncnt + 1))     /* Missing required format string */
  1189. X          Usage ();
  1190. X        thefmtstr = argv[ncnt];    /* Else our format string is the next arg */
  1191. X        ncnt += 1;                 /* Bump arg counter */
  1192. X        LSFlags |= LONGLIST;
  1193. X        break;
  1194. X      case 'H':
  1195. X        LSFlags |= NOHEADERS;
  1196. X        break;
  1197. X      case 'I':
  1198. X        LSFlags |= NOINTERACT;
  1199. X        break;
  1200. X      case 'M':
  1201. X        LSFlags |= MIXFILESDIRS;
  1202. X        break;
  1203. X      case 'N':
  1204. X        if (argc < (ncnt + 1))     /* Missing required name string */
  1205. X          Usage ();
  1206. X        else
  1207. X        {
  1208. X          if (GetFileDate (argv[ncnt], &thenewdate) == 0)
  1209. X            NoFileExit (argv[ncnt]);
  1210. X          ncnt += 1;                 /* Bump arg counter */
  1211. X          LSFlags |= SHOWNEWERTHAN;
  1212. X        }
  1213. X        break;
  1214. X      case 'O':
  1215. X        if (argc < (ncnt + 1))     /* Missing required name string */
  1216. X        {
  1217. X          Usage ();
  1218. X        }
  1219. X        else
  1220. X        {
  1221. X          if (GetFileDate (argv[ncnt], &theolddate) == 0)
  1222. X            NoFileExit (argv[ncnt]);
  1223. X          ncnt += 1;                 /* Bump arg counter */
  1224. X          LSFlags |= SHOWOLDERTHAN;
  1225. X        }
  1226. X        break;
  1227. X      case 'P':
  1228. X        LSFlags |= (FULLPATHNAMES | LONGLIST);
  1229. X        break;
  1230. X      case 'R':
  1231. X        LSFlags |= LISTALL;
  1232. X        break;
  1233. X      case 'T':
  1234. X        LSFlags |= TOTALIZE;
  1235. X        break;
  1236. X      case 'X':
  1237. X        if (argc < (ncnt + 1))     /* Missing required width number */
  1238. X        {
  1239. X          Usage ();
  1240. X        }
  1241. X        else
  1242. X        {
  1243. X          (VOID) GetDecNum (argv[ncnt], &CurWinCols);
  1244. X          ncnt += 1;                 /* Bump arg counter */
  1245. X        }
  1246. X        break;
  1247. X      case 'Y':
  1248. X        if (argc < (ncnt + 1))     /* Missing required height number */
  1249. X        {
  1250. X          Usage ();
  1251. X        }
  1252. X        else
  1253. X        {
  1254. X          (VOID) GetDecNum (argv[ncnt], &CurWinRows);
  1255. X          ncnt += 1;                 /* Bump arg counter */
  1256. X        }
  1257. X        break;
  1258. X      case 'c':
  1259. X        LSFlags |= LONGLIST | NOTEFLAG;
  1260. X        break;
  1261. X      case 'd':
  1262. X        LSFlags &= ~SHOWFILES;
  1263. X        break;
  1264. X      case 'f':
  1265. X        LSFlags &= ~SHOWDIRS;
  1266. X        break;
  1267. X      case 'k':
  1268. X        LSFlags &= ~CONSOLE;
  1269. X        break;
  1270. X      case 'l':
  1271. X        LSFlags |= LONGLIST;
  1272. X        break;
  1273. X      case 'n':
  1274. X        LSFlags |= NOSORTFLAG;
  1275. X        break;
  1276. X      case 'p':
  1277. X        procp = (struct Process *) FindTask (0L);
  1278. X        procp->pr_WindowPtr = OldWindowPtr;
  1279. X        break;
  1280. X      case 'r':
  1281. X        LSFlags |= REVFLAG;
  1282. X        break;
  1283. X      case 's':
  1284. X        sortkey = 1;
  1285. X        break;
  1286. X      case 't':
  1287. X        sortkey = 2;
  1288. X        break;
  1289. X      case 'v':
  1290. X        if (argc < (ncnt + 1))         /* Missing required pattern string */
  1291. X          Usage ();
  1292. X        theAntiPat = argv[ncnt];    /* Point to pattern area */
  1293. X        ncnt += 1;            /* Skip over arg */
  1294. X        LSFlags |= ANTIMATCH;        /* Set flag to use antipat */
  1295. X        break;
  1296. X      default:
  1297. X        tmpmsg[0] = argv[cnt][i];
  1298. X        tmpmsg[1] = 0;
  1299. X        (VOID) asprintf (workstr, BadOptFmtStr, tmpmsg);
  1300. X        WSTR (workstr);
  1301. X        Usage ();
  1302. X        break;
  1303. X    }
  1304. X  }
  1305. X  return(ncnt);
  1306. X}
  1307. X
  1308. X/* -------------------------------------------------------------------- */
  1309. XVOID _main (line)
  1310. X  BYTE *line;
  1311. X{
  1312. X  BYTE *argv[MAXARG];             /* arg pointers */
  1313. X  LONG argc;                      /* arg count */
  1314. X  LONG cnt;
  1315. X  struct Process *procp;
  1316. X
  1317. X/* Prevent system request from occuring by default */
  1318. X  procp = (struct Process *) FindTask (0L);
  1319. X  OldWindowPtr = procp->pr_WindowPtr;
  1320. X  procp->pr_WindowPtr = (APTR)-1L;
  1321. X  
  1322. X/* Construct list of args */
  1323. X  GetCLIArgs (line, &argc, argv);
  1324. X
  1325. X/* Grab FileHandles for input and output to console (or redirection file) */
  1326. X  ConIn = (struct FileHandle *)Input ();
  1327. X  ConOut = (struct FileHandle *)Output ();
  1328. X
  1329. X/* Is this a CLI? Set a flag */
  1330. X  if (IsInteractive ((BPTR)ConOut) != 0 && IsInteractive ((BPTR)ConIn) != 0)
  1331. X    LSFlags |= CONSOLE;
  1332. X
  1333. X/* Allocate a global FileInfoBlock for ExNext() */
  1334. X  if ((GFibp = myalloc ((LONG) sizeof (struct FileInfoBlock))) == 0)
  1335. X    NoMemExit ();
  1336. X
  1337. X/* Initialize arg count, zero grand totals */
  1338. X  cnt = 1;
  1339. X  gtotblocks = gtotbytes = gdircount = gfilecount = 0;
  1340. X
  1341. X/* Parse command line arguments, if any */
  1342. X  do
  1343. X  {
  1344. X    if (cnt < argc && argv[cnt][0] == '-')
  1345. X    {
  1346. X    /* Reset for next arg */ 
  1347. X      LSFlags &= CONSOLE;
  1348. X      LSFlags |= SHOWDIRS | SHOWFILES;
  1349. X      thefmtstr = deffmtstr;
  1350. X
  1351. X      do
  1352. X      {
  1353. X        cnt = ParseCmdOptions(cnt, argc, argv);
  1354. X      } while (cnt < argc && argv[cnt][0] == '-');
  1355. X    }
  1356. X
  1357. X  /* Is there an named path arg to do? */
  1358. X    if (cnt < argc)
  1359. X    {
  1360. X      LSFlags |= PATHNAMED;                  /* Flag that we have a name */
  1361. X      theFilePat[0] = 0;                     /* Terminate pattern strings */
  1362. X      theDirPat[0] = 0;
  1363. X      (VOID) stpcpy (thePath, argv[cnt]);    /* Copy this arg to work space */
  1364. X      cnt++;                                 /* Advance arg counter */
  1365. X
  1366. X    /* Wild pathname? Separate into components until we find a non-wild path */
  1367. X      if (iswild (thePath) != 0)
  1368. X      {
  1369. X        (VOID) GetFileString (theFilePat, thePath);
  1370. X        (VOID) stpcpy (theDirPat, theFilePat);
  1371. X        (VOID) GetPathString (thePath, thePath);
  1372. X
  1373. X      /* Still wild?  First part must be wild filename to match */
  1374. X        if (iswild (thePath) != 0)
  1375. X        {
  1376. X          (VOID) GetFileString (theDirPat, thePath);
  1377. X          (VOID) GetPathString (thePath, thePath);
  1378. X        }
  1379. X      }
  1380. X#ifdef DEBUGIT
  1381. X      asprintf(workstr, "path: %s\n", thePath); WSTR (workstr);
  1382. X      asprintf(workstr, " dir: %s\n", theDirPat); WSTR (workstr);
  1383. X      asprintf(workstr, "file: %s\n", theFilePat); WSTR (workstr);
  1384. X#endif
  1385. X
  1386. X    /* No wildcards allowed in the final pathname! */
  1387. X      if (iswild (thePath) != 0)
  1388. X        CleanUp (NoWildPathMsg, 20L, 120L);
  1389. X
  1390. X    /* Now try to lock the dir (or file) */
  1391. X      if ((CurFLock = (struct FileLock *)Lock (thePath, (LONG)ACCESS_READ)) == 0)
  1392. X      {
  1393. X        NoFileExit (thePath);
  1394. X      }
  1395. X    }
  1396. X    else
  1397. X    {
  1398. X    /*
  1399. X     * If no filename was specified, steal Lock on current directory from
  1400. X     * CLI process task info.  We durn well better get something useful back;
  1401. X     * since we don't do any error checking on the "borrowed" Lock.
  1402. X     */
  1403. X      CurFLock = (struct FileLock *)procp->pr_CurrentDir;
  1404. X    }
  1405. X
  1406. X  /* Make a full pathname string from given CurFLock if no colon in path */
  1407. X    if (aindex(thePath, ':') == 0)
  1408. X      MakePathString (CurFLock, thePath);
  1409. X    curpath = thePath;
  1410. X
  1411. X#ifdef DEBUGIT
  1412. X    asprintf(workstr, "Final path: %s\n", thePath); WSTR (workstr);
  1413. X#endif
  1414. X
  1415. X  /* If there isn't a dir pattern or file pattern specified, match everything */
  1416. X    if (theDirPat[0] == 0)
  1417. X    {
  1418. X      theDirPat[0] = '*';               /* "*" default matchall dir pattern */
  1419. X      theDirPat[1] = 0;                 /* Null terminate string */
  1420. X    }
  1421. X
  1422. X    if (theFilePat[0] == 0)
  1423. X    {
  1424. X      theFilePat[0] = '*';
  1425. X      theFilePat[1] = 0;
  1426. X    }
  1427. X
  1428. X  /* Get the directory for this path, display it */
  1429. X    DirIt (CurFLock, thePath);
  1430. X
  1431. X  /* Release the lock if we locked it */
  1432. X    if (CurFLock != 0 && (LSFlags & PATHNAMED) != 0)
  1433. X    {
  1434. X      UnLock ((BPTR)CurFLock);
  1435. X    }
  1436. X    CurFLock = 0;
  1437. X
  1438. X  /* Still more args? Put a linefeed between listing outputs
  1439. X    if (cnt < argc && (LSFlags & NOHEADERS) == 0)
  1440. X      WCHR (NLine);
  1441. X   */
  1442. X    TestBreak();
  1443. X    if ((LSFlags & TOTALIZE) != 0)
  1444. X    {
  1445. X      SetConPen (penstr4);
  1446. X      WSTR (TotHeaderMsg);
  1447. X      SetConPen (penstr5);
  1448. X      (VOID) asprintf (workstr, totalfmtstr, gdircount, gfilecount, gtotblocks, gtotbytes);
  1449. X      WSTR (workstr);
  1450. X      SetConPen (penstr0);
  1451. X    }
  1452. X  } while (cnt < argc && (LSFlags & BREAKFLAG) == 0);
  1453. X
  1454. X/* All done! Clean exit */
  1455. X  CleanUp (&NLine[1], 0L, 0L);
  1456. X}
  1457. END_OF_FILE
  1458. if test 43452 -ne `wc -c <'src/ls.c'`; then
  1459.     echo shar: \"'src/ls.c'\" unpacked with wrong size!
  1460. fi
  1461. # end of 'src/ls.c'
  1462. fi
  1463. echo shar: End of archive 3 \(of 3\).
  1464. cp /dev/null ark3isdone
  1465. MISSING=""
  1466. for I in 1 2 3 ; do
  1467.     if test ! -f ark${I}isdone ; then
  1468.     MISSING="${MISSING} ${I}"
  1469.     fi
  1470. done
  1471. if test "${MISSING}" = "" ; then
  1472.     echo You have unpacked all 3 archives.
  1473.     rm -f ark[1-9]isdone
  1474. else
  1475.     echo You still need to unpack the following archives:
  1476.     echo "        " ${MISSING}
  1477. fi
  1478. ##  End of shell archive.
  1479. exit 0
  1480. -- 
  1481. Mail submissions (sources or binaries) to <amiga@cs.odu.edu>.
  1482. Mail comments to the moderator at <amiga-request@cs.odu.edu>.
  1483. Post requests for sources, and general discussion to comp.sys.amiga.
  1484.